const {SerialPort} = require('serialport')

const devFlag = 5599
const usbPortPath = '/dev/tty.usbserial-110'
// const usbPortPath = 'COM5'


var RecvMsg = {
    createReqNew: function (data_arr) {
        var recvMsg = {}
        recvMsg.head = data_arr[0]
        recvMsg.length = data_arr[1]
        recvMsg.index = data_arr[2]
        recvMsg.cmd = data_arr[3]
        if (recvMsg.length > 3) {
            recvMsg.real_data = data_arr.slice(4, data_arr.length - 2)
        }
        recvMsg.checksum = data_arr[data_arr.length - 2]
        recvMsg.end = data_arr[data_arr.length - 1]

        return recvMsg
    },
    createRespNew: function (cmd, data_arr) {
        let send_data = []
        send_data[0] = 0xAA
        send_data[1] = data_arr.length + 3
        send_data[2] = 0x02
        send_data[3] = cmd
        let checum_arr = []
        let checksum = null
        switch (cmd) {
            case 0x01:
            //查询链接状态
            case 0x03:
            //上传账目增量
            case 0x13:
                //查询链接状态
                data_add(send_data, data_arr)
                checum_arr.push(send_data[1])
                checum_arr.push(send_data[2])
                checum_arr.push(send_data[3])
                data_add(checum_arr, data_arr)
                checksum = xorChecksum(checum_arr)
                send_data[4 + data_arr.length] = checksum
                break;
            case 0x10:
            //获取二维码
            case 0x19:
                //查询支付盒子网络状态
                checum_arr.push(send_data[1])
                checum_arr.push(send_data[2])
                checum_arr.push(send_data[3])
                checksum = xorChecksum(checum_arr)
                send_data[4] = checksum
                break;
            case 0x11:
                break;
            default:
                break;
        }
        send_data[5 + data_arr.length] = 0xDD
        console.log("主板=》盒子：" + send_data)
        console.log("主板=》盒子(16x)：" + decimalArrayToHexStr(send_data).toUpperCase())
        return send_data;
    }
}

function writeToPort(cmd, errorFn) {
    port.write(new Uint8Array(cmd), error => {
        if (error) {
            console.log('写入port异常:' + error)
            errorFn(error)
        }
    })
}

function buffer2Array(buf) {
    const arr = []
    for (let i = 0; i < buf.length; i++) {
        arr.push(buf[i])
    }
    return arr
}

function data_add(ori_data, add_data) {
    for (let i = 0; i < add_data.length; i++) {
        ori_data.push(add_data[i])
    }
}

function xorChecksum(data) {
    let sum = 0;
    data.forEach(item => {
        sum ^= item;
    });
    return sum;
}

function decimalArrayToHexStr(decimalArray) {
    const hexArray = decimalArray.map(decimal => decimal.toString(16)).join(``)
    return hexArray;
}

/**
 * 小端模式16进制数组转十进制
 * @param hexArray
 * @returns {number}
 */
function littleEndianHexToDecimal(hexArray) {
    let decimalNumber = 0;
    for (let i = 0; i < hexArray.length; i++) {
        decimalNumber += hexArray[i] * Math.pow(256, i);
    }
    return decimalNumber;
}

/**
 * 小端模式转换十进制
 * @param number
 * @param byteCount
 * @returns {*[]}
 */
function numberToLittleEndianBytes(number, byteCount) {
    const bytes = [];
    for (let i = 0; i < byteCount; i++) {
        bytes.push(number & 0xFF);
        number >>= 8;
    }
    return bytes;
}

//byte转string
function byteToString(arr) {
    if (typeof arr === 'string') {
        return arr;
    }
    var str = '', _arr = arr;
    for (var i = 0; i < _arr.length; i++) {
        var one = _arr[i].toString(2), v = one.match(/^1+?(?=0)/);
        if (v && one.length == 8) {
            var bytesLength = v[0].length;
            var store = _arr[i].toString(2).slice(7 - bytesLength);
            for (var st = 1; st < bytesLength; st++) {
                store += _arr[st + i].toString(2).slice(2);
            }
            str += String.fromCharCode(parseInt(store, 2));
            i += bytesLength - 1;
        } else {
            str += String.fromCharCode(_arr[i]);
        }
    }
    return str;
}

function recv_data(data_arr, error) {
    if (data_arr == null || data_arr.length <= 0) {
        return
    }
    //校验开头
    let head = data_arr[0]
    if (head != 0xAA) {
        error("数据异常，帧头固定为0xAA")
        return
    }
    //校验长度
    let length = data_arr[1]
    if (data_arr.length != length + 3) {
        error("长度异常")
        return
    }
    //校验异或
    let numbers = data_arr.slice(1, data_arr.length - 2);
    if (xorChecksum(numbers) != data_arr[data_arr.length - 2]) {
        error("校验异或异常")
        return
    }
    //校验尾数
    if (data_arr[data_arr.length - 1] != 0xDD) {
        error("结束符异常")
        return
    }

    let recvMsg = RecvMsg.createReqNew(data_arr)
    if (recvMsg.index == 0x01) {
        console.log("盒子=>主板指令:" + recvMsg.cmd)
        switch (recvMsg.cmd) {
            case 0x01:
                //查询链接状态 支付盒子 -------------> 游戏机主板
                console.log('查询链接状态支付盒子 -------------> 游戏机主板:' + recvMsg.real_data)
                console.log('查询链接状态支付盒子 -------------> 游戏机主板:' + decimalArrayToHexStr(recvMsg.real_data))
                powerOn(devFlag)
                break
            case 0x03:
                //云上分 支付盒子 -------------> 游戏机主板
                console.log('云上分支付盒子 -------------> 游戏机主板:' + recvMsg.real_data)
                //自增码
                let increNum = recvMsg.real_data[0]
                let fentime = littleEndianHexToDecimal(recvMsg.real_data.slice(1, 2))
                let fenmount = littleEndianHexToDecimal(recvMsg.real_data.slice(3, 2))
                console.log("自增码：" + increNum + ",分钟数：" + fentime + ",金额：" + fenmount)
                let success = yunFenReq(increNum, fentime, fenmount)
                yunFenResp(success)
                break
            case 0x10:
                //获取二维码链接 支付盒子 -------------> 游戏机主板
                let url = byteToString(recvMsg.real_data)
                qrCodeRecv(url)
                break
            case 0x19:
                //查询支付盒子网络状态 支付盒子 -------------> 游戏机主板
                let sign = recvMsg.real_data[0]
                let status = recvMsg.real_data[1]
                boxStatusResp(sign, status)
                break
            default:
                break;
        }
    }
}

// 打印串口列表
SerialPort.list().then((ports) => {
    console.log(ports);
}).catch((err) => {
    console.log(err);
})
//初始化port
const port = new SerialPort({
    path: usbPortPath,
    baudRate: 38400,
    dataBits: 8,
    stopBits: 1,
    parity: 'none',
    autoOpen: false
}, function (err) {
    if (err) {
        return console.log('连接串口Error: ', err.message)
    }
})
//错误监听
port.on('error', (error) => {
    console.log('error: ' + error)
})
// 监听收到的数据
let start = false
let byte_data = [];
port.on('data', function (data) {
        var bufferArry = buffer2Array(data)
        if (bufferArry[0] === 0xAA && !start) {
            start = true
            byte_data = []
        }
        if (start) {
            data_add(byte_data, bufferArry)
            if (bufferArry[bufferArry.length - 1] === 0xDD) {
                start = false
                //结束
                console.log('盒子=》主板：' + byte_data)
                console.log('盒子=》主板(16x)：' + decimalArrayToHexStr(byte_data).toUpperCase())
                recv_data(byte_data, err => {
                    console.log('处理错误:' + err)
                })
            }
        }
    }
)
const yunFenResp = (success) => {
    let dat_arr = []
    if (success) {
        dat_arr[0] = 0x01
    } else {
        dat_arr[0] = 0x00
    }
    let cmd = RecvMsg.createRespNew(0x03, dat_arr)
    writeToPort(cmd, port)
}
const powerOn = (flag) => {
    let num_bit = numberToLittleEndianBytes(flag, 2)
    let cmd = RecvMsg.createRespNew(0x01, num_bit)
    writeToPort(cmd, port)
}
/**
 * 主板=》盒子 查询设备状态
 */
const queryBoxStatus = () => {
    let cmd = RecvMsg.createRespNew(0x19, [])
    writeToPort(cmd, port)
}
/**
 *  上传账目
 * @param errCode 错误代码
 * @param incCoin 增量投币数
 * @param reNum 退礼数
 * @param income 收入
 * @param other 预留
 */
const uploadAccountItems = (errCode, incCoin, reNum, income, other) => {
    let data_arr = []
    data_arr[0] = 0x1A
    data_arr[1] = errCode
    data_arr[2] = incCoin
    data_arr[3] = reNum
    data_arr[4] = income
    data_arr[5] = 0
    data_arr[6] = 0
    if (other) {
        let arr = numberToLittleEndianBytes(other, 2)
        data_arr[5] = arr[0]
        data_arr[6] = arr[1]
    }

    let cmd = RecvMsg.createRespNew(0x13, data_arr)
    writeToPort(cmd, port)
}
const qrCodeReq = () => {
    let cmd = RecvMsg.createRespNew(0x10, [])
    writeToPort(cmd)
}

//*************************BUS start*********************

/**
 * 支付成功后调用
 * @param increNum 自增码
 * @param fentime 单位秒 投币数
 * @param fenmount 金额
 * @return 成功，失败
 */
const yunFenReq = (increNum, fentime, fenmount) => {
    console.log("云上分", increNum, fenmount, fentime)
    return true
}
/**
 * 支付地址
 * @param url
 */
const qrCodeRecv = (url) => {
    console.log("支付地址：" + url)
}
/**
 * 设备状态
 * @param sign
 * @param status
 */
const boxStatusResp = (sign, status) => {
    console.log("信号：" + sign + ",状态:" + status)
}

//*************************BUS end*********************

//开启端口
port.open(err => {
    console.log('port opened success...')
    if (err) {
        console.log("open port error " + err)
    }
})

module.exports = {
    /**
     * 二维码 主板-》盒子
     */
    qrCodeReq,
    /**
     * 上传账目信息
     */
    uploadAccountItems,
    /**
     * 查询设备状态
     */
    queryBoxStatus
}
